package se.rende.myrss;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import se.rende.myrss.data.Channel;
import se.rende.myrss.data.ChannelItem;
import se.rende.myrss.data.DataService;
import se.rende.myrss.data.Filter;

import com.sun.syndication.feed.synd.SyndContentImpl;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndEntryImpl;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.feed.synd.SyndFeedImpl;
import com.sun.syndication.io.FeedException;
import com.sun.syndication.io.SyndFeedOutput;

/**
 * Returns an aggregated RSS 2.0 feed with items from the 
 * datastore for the channel urls supplied in ch parameters 
 * with respect to the optional since parameters.
 */
@SuppressWarnings("serial")
public class FetchServlet extends HttpServlet {
	private static final SimpleDateFormat headerDateFormat = new SimpleDateFormat(
			"EEE MMM dd HH:mm:ss z yyyy", Locale.US);
	private DataService dataService;

	@Override
	public void init(ServletConfig config) throws ServletException {
		dataService = (DataService) config.getServletContext().getAttribute(
				"data");
	}

	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws IOException {
		try {
			System.out.println("req.getQueryString()=" + req.getQueryString());
	
			String ifModifiedSince = req.getHeader("If-Modified-Since");
	
			// get all channel params, and parallel since dates
			String[] urls = req.getParameterValues("ch");
			String[] sinces = req.getParameterValues("since");
			if (urls != null && urls.length > 0) {
				resp.setContentType("text/plain");
				resp.setCharacterEncoding("utf-8");
	
				SyndFeed aggrFeed = new SyndFeedImpl();
				aggrFeed.setFeedType("rss_2.0_withSource");
	
				aggrFeed.setDescription("Anonymous Aggregated Feed");
				aggrFeed.setAuthor("anonymous");
				aggrFeed.setLink("http://myrss-server.appspot.com");
				StringBuilder channelTitleBuilder = new StringBuilder();
	
				List aggrEntries = new ArrayList();
				aggrFeed.setEntries(aggrEntries);
	
				List<Channel> channels = dataService.getChannels();
				for (int i = 0; i < urls.length; i++) {
					String url = urls[i];
					System.out.println("FetchServlet.doGet() url=" + url);
					try {
						Channel channel = findChannelByUrl(channels, url);
						if (channelTitleBuilder.length() > 0) {
							channelTitleBuilder.append("&");
						}
						if (channel != null) {
							channelTitleBuilder.append(URLEncoder.encode(
									channel.getName(), "utf-8"));
							
							if (urls != null && sinces != null && urls.length == sinces.length) {
								ifModifiedSince = sinces[i];
							}
							Date ifModifiedSinceDate = new Date(0L);
							if (ifModifiedSince != null) {
								ifModifiedSinceDate = headerDateFormat.parse(ifModifiedSince);
							}
							System.out.println("FetchServlet.doGet() channel.getId()=" + channel.getId());
							for (ChannelItem item : dataService.getItems(channel.getId())) {
								if (item.getDate().after(ifModifiedSinceDate)) {
									SyndEntryImpl entry = new SyndEntryImpl();
									// create this only to be able to set item
									// source
									SyndFeedImpl feed = new SyndFeedImpl();
									feed.setTitle(channel.getName());
									entry.setSource(feed);
									
									entry.setTitle(item.getTitle());
									entry.setLink(item.getLink());
									entry.setPublishedDate(item.getDate());
									entry.setAuthor(item.getAuthor());
									SyndContentImpl description = new SyndContentImpl();
									description.setType("text/html");
									description.setValue(item.getDescription());
									entry.setDescription(description);
									aggrEntries.add(entry);
								}
							}
						}
					} catch (IllegalArgumentException e) {
						throw new IOException("can't serve rss", e);
					}
				}
				aggrFeed.setTitle(channelTitleBuilder.toString());
				SyndFeedOutput output = new SyndFeedOutput();
				ServletOutputStream os = resp.getOutputStream();
				PrintWriter writer = new PrintWriter(new OutputStreamWriter(os,
						"utf-8"));
	
				try {
					output.output(aggrFeed, writer);
				} catch (FeedException e) {
					throw new IOException("can't serve rss", e);
				}
				writer.close();
			}
		} catch (ParseException e) {
			throw new IOException("can't serve rss", e);
		}
	}

	private Channel findChannelByUrl(List<Channel> channels, String url) {
		for (Channel channel : channels) {
			if (url != null && url.equals(channel.getUrl())) {
				return channel;
			}
		}
		return null;
	}

	private void filterEntry(String url, SyndEntry syndEntry) {
		String s = syndEntry.getDescription().getValue();
		List<Filter> filters = dataService.getFiltersByUrl(url);
		for (Filter filter : filters) {
			s = filter.execute(s, syndEntry);
		}
		syndEntry.getDescription().setValue(s);
	}

}
